/*******************************************************************************
 * Copyright (c) 2005, 2007 Intel Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Intel Corporation - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.enablement;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.cdt.managedbuilder.core.BuildException;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IFolderInfo;
import org.eclipse.cdt.managedbuilder.core.IHoldsOptions;
import org.eclipse.cdt.managedbuilder.core.IManagedConfigElement;
import org.eclipse.cdt.managedbuilder.core.IOption;
import org.eclipse.cdt.managedbuilder.core.IResourceInfo;
import org.eclipse.cdt.managedbuilder.core.ITool;
import org.eclipse.cdt.managedbuilder.core.IToolChain;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;

public class OptionEnablementExpression extends AndExpression{
	public static final String NAME = "enablement"; 	//$NON-NLS-1$
	public static final String TYPE = "type"; 	//$NON-NLS-1$

	public static final String TYPE_UI_VISIBILITY = "UI_VISIBILITY"; 	//$NON-NLS-1$
	public static final String TYPE_UI_ENABLEMENT = "UI_ENABLEMENT"; 	//$NON-NLS-1$
	public static final String TYPE_CMD_USAGE = "CMD_USAGE"; 	//$NON-NLS-1$
	public static final String TYPE_CONTAINER_ATTRIBUTE = "CONTAINER_ATTRIBUTE"; 	//$NON-NLS-1$
	public static final String TYPE_ALL = "ALL"; 	//$NON-NLS-1$

	public static final String FLAG_DELIMITER = "|"; 	//$NON-NLS-1$

	public static final String ATTRIBUTE = "attribute"; 	//$NON-NLS-1$
	public static final String VALUE = "value"; 	//$NON-NLS-1$
	
	public static final String EXTENSION_ADJUSTMENT = "extensionAdjustment"; 	//$NON-NLS-1$
	
	public static final String YES = "yes"; 	//$NON-NLS-1$
	public static final String TRUE = "true"; 	//$NON-NLS-1$

	
	private static final String fSupportedOptionAttributes[] = {
		IOption.COMMAND,
		IOption.COMMAND_FALSE,
		IOption.VALUE,
		IOption.DEFAULT_VALUE,
	};

	private static final String fSupportedToolChainAttributes[] = {
		IToolChain.TARGET_TOOL,
	};

	private static final String fSupportedConfigurationAttributes[] = {
		IConfiguration.EXTENSION,
		IConfiguration.ARTIFACT_NAME,
	};

	public static final int FLAG_UI_VISIBILITY = 0x01;
	public static final int FLAG_UI_ENABLEMENT = 0x02;
	public static final int FLAG_CMD_USAGE = 0x04;
	public static final int FLAG_CONTAINER_ATTRIBUTE = 0x08;
	public static final int FLAG_ALL = ~0;

	private int fEnablementFlags;
	private String fAttribute;
	private String fValue;
	private boolean fIsExtensionAdjustment;

	public OptionEnablementExpression(IManagedConfigElement element) {
		super(element);
		
		fEnablementFlags = calculateFlags(element.getAttribute(TYPE));
		
		fAttribute = element.getAttribute(ATTRIBUTE);
		fValue = element.getAttribute(VALUE);
		String tmp = element.getAttribute(EXTENSION_ADJUSTMENT);
		
		adjustAttributeSupport();
		
		if(tmp != null) {
			fIsExtensionAdjustment = getBooleanValue(tmp);
		} else {
			fIsExtensionAdjustment = checkFlags(FLAG_CONTAINER_ATTRIBUTE) ?
					false : true;
		}
	}
	
	private void adjustAttributeSupport(){
		boolean cleanAttrFlag = true;
		if(fAttribute != null && fValue != null){
			for(int i = 0; i < fSupportedOptionAttributes.length; i++){
				if(fAttribute.equals(fSupportedOptionAttributes[i])){
					cleanAttrFlag = false;
					break;
				}
			}
			
			if(cleanAttrFlag){
				for(int i = 0; i < fSupportedToolChainAttributes.length; i++){
					if(fAttribute.equals(fSupportedToolChainAttributes[i])){
						cleanAttrFlag = false;
						break;
					}
				}
			}

			if(cleanAttrFlag){
				for(int i = 0; i < fSupportedConfigurationAttributes.length; i++){
					if(fAttribute.equals(fSupportedConfigurationAttributes[i])){
						cleanAttrFlag = false;
						break;
					}
				}
			}

		}
		
		if(cleanAttrFlag){
			fEnablementFlags &= ~FLAG_CONTAINER_ATTRIBUTE;
			fAttribute = null;
			fValue = null;
		}
	}

	public String[] convertToList(String value, String delimiter){
		List<String> list = new ArrayList<String>();
		int delLength = delimiter.length();
		int valLength = value.length();

		if(delLength == 0){
			list.add(value);
		}
		else{
			int start = 0;
			int stop;
			while(start < valLength){
				stop = value.indexOf(delimiter,start);
				if(stop == -1)
					stop = valLength;
				String subst = value.substring(start,stop);
				list.add(subst);
				start = stop + delLength;
			}
		}

		return list.toArray(new String[list.size()]);
	}
	
	protected int calculateFlags(String flagsString){
		int flags = 0;
		
		if(flagsString != null){
			String strings[] = convertToList(flagsString,FLAG_DELIMITER);
			
			for(int i = 0; i < strings.length; i++){
				String str = strings[i].trim();
				if(TYPE_UI_VISIBILITY.equals(str))
					flags |= FLAG_UI_VISIBILITY;
				else if(TYPE_UI_ENABLEMENT.equals(str))
					flags |= FLAG_UI_ENABLEMENT;
				else if(TYPE_CMD_USAGE.equals(str))
					flags |= FLAG_CMD_USAGE;
				else if(TYPE_CONTAINER_ATTRIBUTE.equals(str))
					flags |= FLAG_CONTAINER_ATTRIBUTE;
				else if(TYPE_ALL.equals(str))
					flags |= FLAG_ALL;
			}
		}
		
		if(flags == 0)
			flags = FLAG_ALL;
		return flags;
	}
	
	public boolean evaluate(IResourceInfo rcInfo, 
            IHoldsOptions holder, 
            IOption option,
            int flags){
		return evaluate(rcInfo, holder, option, flags, (FLAG_CONTAINER_ATTRIBUTE & flags) == 0);
	}

	public boolean evaluate(IResourceInfo rcInfo, 
            IHoldsOptions holder, 
            IOption option,
            int flags,
            boolean bDefault){
		return checkFlags(flags) ? evaluate(rcInfo, holder, option)
				: bDefault;
	}

/*	public boolean evaluate(IBuildObject configuration, 
            IHoldsOptions holder, 
            IOption option) {
		if(getChildren().length == 0)
			return false;
		return super.evaluate(configuration,holder,option);
	}
*/	
	public boolean checkFlags(int flags){
		return (fEnablementFlags & flags) == flags;
	}
	
	public int getFlags(){
		return fEnablementFlags;
	}
	
/*	public boolean performAdjustment(IBuildObject configuration, 
            IHoldsOptions holder, 
            IOption option,
            boolean extensionAdjustment){
		boolean adjusted = false;
		if(canPerformAdjustment(extensionAdjustment)){
			if(evaluate(configuration,holder,option,FLAG_CONTAINER_ATTRIBUTE)){
				if(option != null){
					adjusted = adjustOption(configuration, holder, option, extensionAdjustment);
				} else if (holder != null){
					if(holder instanceof ITool){
						adjusted = adjustTool(configuration, (ITool)holder, extensionAdjustment);
					} else if(holder instanceof IToolChain){
						adjusted = adjustToolChain(configuration, (IToolChain)holder, extensionAdjustment);
					}
				} else if (configuration != null){
					if(configuration instanceof IConfiguration){
						adjusted = adjustConfiguration((IConfiguration)holder, extensionAdjustment);
					}
				}
			}
		}
		return adjusted;
	}
*/	
	private static IOption getOptionToSet(IHoldsOptions holder, 
			IOption option, 
			String value, 
			boolean extensionAdjustment){
		IOption optionToSet = null;
		if(value != null){
			try {
				optionToSet = holder.getOptionToSet(option, extensionAdjustment);
			} catch (BuildException e) {
			}
		} else {
			if(!extensionAdjustment && option.getOptionHolder() == holder){
				optionToSet = option;
			}
		}
		return optionToSet;
	}

	public static boolean adjustOption(IResourceInfo rcInfo, 
            IHoldsOptions holder, 
            IOption option,
            String attribute,
            String value,
            boolean extensionAdjustment
            ){
		
		if(value == null && rcInfo.isExtensionElement())
			return false;
		
		IOption setOption = getOptionToSet(holder, option, value, extensionAdjustment);
		if(setOption == null)
			return false;
			
		boolean adjusted = true;
		try{
			if(IOption.COMMAND.equals(attribute)){
//				IOption setOption = holder.getOptionToSet(option, extensionAdjustment);
				setOption.setCommand(value);
			}else if(IOption.COMMAND_FALSE.equals(attribute)){
//				IOption setOption = holder.getOptionToSet(option, extensionAdjustment);
				setOption.setCommandFalse(value);
			}else if(IOption.VALUE.equals(attribute)){
//				IOption setOption = holder.getOptionToSet(option, extensionAdjustment);
				switch(setOption.getValueType()){
				case IOption.BOOLEAN:
					Boolean bValue = value != null ? Boolean.valueOf(value) : null;
					if(extensionAdjustment)
						setOption.setValue(bValue);
					else{ 
						if (bValue == null) {
							IOption superOption = setOption.getSuperClass();
							if(superOption != null){
								bValue = Boolean.valueOf(superOption.getBooleanValue());
							}
						}

						if(bValue != null)
							ManagedBuildManager.setOption(rcInfo, holder, setOption, bValue.booleanValue());
						else
							setOption.setValue((Object)null);
					}
					break;
				case IOption.ENUMERATED:
				case IOption.TREE:
				case IOption.STRING:
					if(extensionAdjustment)
						setOption.setValue(value);
					else
						ManagedBuildManager.setOption(rcInfo, holder, setOption, value);
					break;
				case IOption.STRING_LIST:
				case IOption.INCLUDE_PATH:
				case IOption.PREPROCESSOR_SYMBOLS:
				case IOption.LIBRARIES:
				case IOption.OBJECTS:
				case IOption.INCLUDE_FILES:
				case IOption.LIBRARY_PATHS:
				case IOption.LIBRARY_FILES:
				case IOption.MACRO_FILES:
				case IOption.UNDEF_INCLUDE_PATH:
				case IOption.UNDEF_PREPROCESSOR_SYMBOLS:
				case IOption.UNDEF_INCLUDE_FILES:
				case IOption.UNDEF_LIBRARY_PATHS:
				case IOption.UNDEF_LIBRARY_FILES:
				case IOption.UNDEF_MACRO_FILES:
					//TODO: add String list value support
					adjusted = false;
					break;
				}
			} else if(IOption.DEFAULT_VALUE.equals(attribute)){
				switch(setOption.getValueType()){
				case IOption.BOOLEAN:
					Boolean bValue = value != null ? Boolean.valueOf(value) : null;
					setOption.setDefaultValue(bValue);
					break;
				case IOption.ENUMERATED:
				case IOption.STRING:
					setOption.setDefaultValue(value);
					break;
				case IOption.STRING_LIST:
				case IOption.INCLUDE_PATH:
				case IOption.PREPROCESSOR_SYMBOLS:
				case IOption.LIBRARIES:
				case IOption.OBJECTS:
					//TODO: add String list value support
					adjusted = false;
					break;
				}
			} else
				adjusted = false;
		}catch (BuildException e){
			adjusted = false;
		}	
		return adjusted;		
	}
	
	public boolean adjustOption(IResourceInfo rcInfo, 
            IHoldsOptions holder, 
            IOption option,
            AdjustmentContext context,
            boolean extensionAdjustment){
		if(!canPerformAdjustment(extensionAdjustment))
			return false;

		boolean needed = adjustmentNeeded(rcInfo, holder, option);
		
		if(context != null)
			context.addAdjustedState(fAttribute, needed);
		
		if(needed)
			return adjustOption(rcInfo, holder, option, fAttribute, fValue, extensionAdjustment);
		return false;
	}
	
	private boolean adjustmentNeeded(IResourceInfo rcInfo, 
            IHoldsOptions holder, 
            IOption option){
		return evaluate(rcInfo,holder,option,FLAG_CONTAINER_ATTRIBUTE);
	}

	private boolean adjustmentNeeded(IFolderInfo info, IToolChain tChain){
		return adjustmentNeeded(info,tChain,null);
	}

	private boolean adjustmentNeeded(IResourceInfo info, ITool tool){
		return adjustmentNeeded(info,tool,null);
	}

	private boolean adjustmentNeeded(IConfiguration cfg){
		return adjustmentNeeded(cfg.getRootFolderInfo(),null,null);
	}

	public static boolean adjustToolChain(IFolderInfo info, 
            IToolChain tChain,
            String attrbute,
            String value,
            boolean extensionAdjustment){
		
		if(value == null && tChain.isExtensionElement())
			return false;
		
		boolean adjusted = true;
		
		if(IToolChain.TARGET_TOOL.equals(attrbute)){
			tChain.setTargetToolIds(value);
		}else{
			adjusted = false;
		}

		return adjusted;
	}

	public boolean adjustToolChain(IFolderInfo info, 
            IToolChain tChain,
            AdjustmentContext context,
            boolean extensionAdjustment){
		if(!canPerformAdjustment(extensionAdjustment))
			return false;
		
		boolean needed = adjustmentNeeded(info, tChain);
		
		if(context != null)
			context.addAdjustedState(fAttribute, needed);

		if(needed)
			return adjustToolChain(info, tChain, fAttribute, fValue, extensionAdjustment);
		return false;
	}

	public static boolean adjustTool(IResourceInfo info, 
            ITool tool,
            String attribute,
            String value,
            boolean extensionAdjustment){
		
		if(value == null && tool.isExtensionElement())
			return false;
		
		return false;
	}
	
	public boolean adjustTool(IResourceInfo info, 
            ITool tool,
            AdjustmentContext context,
            boolean extensionAdjustment){
		if(!canPerformAdjustment(extensionAdjustment))
			return false;
		
		boolean needed = adjustmentNeeded(info, tool);
		
		if(context != null)
			context.addAdjustedState(fAttribute, needed);

		if(needed)
			return adjustTool(info, tool, fAttribute, fValue, extensionAdjustment);
		return false;
	}

	public static boolean adjustConfiguration(IConfiguration cfg,
            String attribute,
            String value,
            boolean extensionAdjustment){

		if(value == null && cfg.isExtensionElement())
			return false;

		boolean adjusted = true;
		
		if(IConfiguration.EXTENSION.equals(attribute)){
			cfg.setArtifactExtension(value);
		} else if(IConfiguration.ARTIFACT_NAME.equals(attribute)){
			cfg.setArtifactName(value);
		} else{
			adjusted = false;
		}

		return adjusted;
	}

	public boolean adjustConfiguration(IConfiguration cfg,
            AdjustmentContext context,
            boolean extensionAdjustment){
		if(!canPerformAdjustment(extensionAdjustment))
			return false;
		
		boolean needed = adjustmentNeeded(cfg);
		
		if(context != null)
			context.addAdjustedState(fAttribute, needed);

		if(needed)
			return adjustConfiguration(cfg, fAttribute, fValue, extensionAdjustment);
		return false;
	}

	public boolean canPerformAdjustment(boolean extensionAdjustment){
		return fIsExtensionAdjustment == extensionAdjustment
				&& checkFlags(FLAG_CONTAINER_ATTRIBUTE);
	}
	
	public static boolean getBooleanValue(String value){
		if(TRUE.equalsIgnoreCase(value))
			return true;
		else if(YES.equalsIgnoreCase(value))
			return true;
		return false;
	}
}
